#include "SimpleImage.h"
#include "../core/Logger.h"

#include <algorithm>
#include <string.h>

SimpleImage::SimpleImage()
{
	_dataRefCt = 0;
	_data = NULL;
	_sizeX = 0;
	_sizeY = 0;
	_sizeC = 0;
	_datasize = 0;
}

SimpleImage::SimpleImage(const SimpleImage& rhs)
{
	_sizeX = rhs._sizeX;
	_sizeY = rhs._sizeY;
	_sizeC = rhs._sizeC;
	_datasize = rhs._datasize;
	_multX = rhs._multX;
	_multY = rhs._multY;
	_multC = rhs._multC;
	_b = rhs._b;
	//_data = new UCHAR[_datasize];
	//memcpy(_data, rhs._data, _datasize * sizeof(UCHAR));
	_data = rhs._data;
	_dataRefCt = rhs._dataRefCt;
	if(_data && _dataRefCt)
		++(*_dataRefCt);
}

SimpleImage::~SimpleImage()
{
	/*
	LOG(logDEBUG) << "Deleting simple image.";
	if(_dataRefCt)
	{
		--(*_dataRefCt);
		if(*_dataRefCt <= 0)
		{
			LOG(logDEBUG) << "Deallocating simple image data.";
			delete[] _data;
			delete _dataRefCt;
		}
	}
	_data = NULL;
	*/
	deallocate();
}

void SimpleImage::deepCopy(const SimpleImage& rhs)
{
	deallocate();
	_sizeX = rhs._sizeX;
	_sizeY = rhs._sizeY;
	_sizeC = rhs._sizeC;
	_datasize = rhs._datasize;
	_multX = rhs._multX;
	_multY = rhs._multY;
	_multC = rhs._multC;
	_b = rhs._b;
	_data = new UCHAR[_datasize];
	memcpy(_data, rhs._data, _datasize * sizeof(UCHAR));
	_dataRefCt = new int(1);
}

void SimpleImage::allocateLayered(int sx, int sy, int sc)
{
	deallocate();

	_datasize = sx * sy * sc;
	_sizeX = sx;
	_sizeY = sy;
	_sizeC = sc;
	_b = 0;
	_multX = 1;
	_multY = sx;
	_multC = (sx * sy);
	_data = new UCHAR[_datasize];
	_dataRefCt = new int(1);
}

void SimpleImage::allocateInterleaved(int sx, int sy, int sc)
{
	deallocate();

	_datasize = sx * sy * sc;
	_sizeX = sx;
	_sizeY = sy;
	_sizeC = sc;
	_b = 0;
	_multX = sc;
	_multY = sx * sc;
	_multC = 1;
	_data = new UCHAR[_datasize];
	_dataRefCt = new int(1);
}

void SimpleImage::deallocate()
{
	if(_dataRefCt)
	{
		--(*_dataRefCt);
		if(*_dataRefCt <= 0)
		{
			LOG(logDEBUG) << "Deallocating simple image data.";
			delete[] _data;
			delete _dataRefCt;
		}
		_data = NULL;
		_dataRefCt = NULL;
	}
	_sizeX = 0;
	_sizeY = 0;
	_sizeC = 0;
	_datasize = 0;
}

void SimpleImage::untether()
{
	if(_data && _dataRefCt)
	{
		UCHAR* oldData = _data;
		_data = new UCHAR[_datasize];
		memcpy(_data, oldData, _datasize * sizeof(UCHAR));
		--(*_dataRefCt);
		if(*_dataRefCt <= 0)
		{
			LOG(logDEBUG) << "Deallocating simple image data.";
			delete[] oldData;
			delete _dataRefCt;
		}
		_dataRefCt = new int(1);
	}
}

SimpleImage SimpleImage::clone()
{
	SimpleImage ret(*this);
	ret.untether();
	return ret;
}

UCHAR SimpleImage::rawGet(int pos)
{
	if(pos < 0 || pos >= _datasize)
		return 0;
	else
		return _data[pos];
}

UCHAR SimpleImage::get(int x, int y, int c)
{
	return rawGet(x * _multX + y * _multY + c * _multC + _b);
}

void SimpleImage::rawSet(int pos, UCHAR val)
{
	if(pos < 0 || pos >= _datasize)
		return;
	else
		_data[pos] = val;
}

void SimpleImage::set(int x, int y, int c, UCHAR val)
{
	rawSet(x * _multX + y * _multY + c * _multC + _b, val);
}

int SimpleImage::rawPos(int x, int y, int c)
{
	return (x * _multX + y * _multY + c * _multC + _b);
}

void SimpleImage::deleteThis()
{
	delete this;
}

int SimpleImage::sizeX()
{
	return _sizeX;
}

int SimpleImage::sizeY()
{
	return _sizeY;
}

int SimpleImage::sizeC()
{
	return _sizeC;
}

int SimpleImage::size()
{
	return _datasize;
}

int SimpleImage::getmx()
{
	return _multX;
}

int SimpleImage::getmy()
{
	return _multY;
}

int SimpleImage::getmc()
{
	return _multC;
}

int SimpleImage::getb()
{
	return _b;
}

int SimpleImage::toBytes(unsigned char* dest, int destsize)
{
	if(destsize < (12 + _datasize))
	{
		return 0;
	}
	// write dimensions
	*((int*)(dest + 0)) = _sizeX;
	*((int*)(dest + 4)) = _sizeY;
	*((int*)(dest + 8)) = _sizeC;
	// write data
	for(int i = 0; i < _datasize; ++i)
		dest[i + 12] = _data[i];
	return 12 + _datasize;
}

void SimpleImage::fromBytes(unsigned char* src, int srcsize)
{
	if(srcsize < 12)
	{
		return;
	}
	// read dimensions
	int sx = *((int*)(src + 0));
	int sy = *((int*)(src + 4));
	int sc = *((int*)(src + 8));
	// allocate
	allocateInterleaved(sx, sy, sc);
	// read data (as much as possible?)
	for(int i = 0; i < _datasize && i + 12 < srcsize; ++i)
		_data[i] = src[i+12];
}

std::string SimpleImage::toStdString()
{
	unsigned char* temp = new unsigned char[_datasize + 12];
	toBytes(temp, _datasize + 12);
	std::string ret((char*)temp, _datasize + 12);
	delete[] temp;
	return ret;
}

void SimpleImage::fromStdString(std::string src)
{
	fromBytes((unsigned char*)src.data(), src.size());
}

void SimpleImage::getBounds(int& x1, int& y1, int& width, int& height, UCHAR thresh, int chan)
{
	x1 = _sizeX - 1;
	int x2 = 0;
	y1 = _sizeY - 1;
	int y2 = 0;

	for(int y = 0; y < _sizeY; ++y)
	{
		for(int x = 0; x < _sizeX; ++x)
		{
			int rpos = x * _multX + y * _multY + chan * _multC + _b;
			if(_data[rpos] > thresh)
			{
				x1 = std::min(x, x1);
				x2 = std::max(x, x2);
				y1 = std::min(y, y1);
				y2 = std::max(y, y2);
			}
		}
	}

	width  = x2 - x1;
	height = y2 - y1;
}

UCHAR* SimpleImage::getPtr()
{
	return _data;
}
